package com.qubi.exchange.biz;


import com.qubi.exchange.constant.OrderDirection;
import com.qubi.exchange.constant.OrderStatus;
import com.qubi.exchange.constant.OrderType;
import com.qubi.exchange.data.MakeOrderParams;
import com.qubi.exchange.mapper.OrderMapper;
import com.qubi.exchange.message.OrderMessage;
import com.qubi.exchange.queue.LinkedBlockingQueueMap;
import com.qubi.exchange.security.auth.client.config.UserAuthConfig;
import com.qubi.exchange.security.auth.client.i18n.ColaLanguage;
import com.qubi.exchange.security.auth.client.jwt.UserAuthUtil;
import com.qubi.exchange.security.common.constant.ResponseCode;
import com.qubi.exchange.security.common.context.BaseContextHandler;
import com.qubi.exchange.security.common.msg.AppResponse;
import com.qubi.exchange.security.common.util.MD5Utils;
import com.qubi.exchange.security.common.util.ReflectionUtils;
import com.qubi.exchange.service.AccountService;
import com.qubi.exchange.service.BalanceService;
import com.qubi.exchange.service.KlineService;
import com.qubi.exchange.service.MatchService;
import com.qubi.exchange.util.InFluxDbService;
import com.qubi.exchange.util.Snowflake;
import com.qubi.me.entity.ColaCoinSymbol;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Log4j2
public class CommonExchangeBiz {
    private Snowflake snowflake = new Snowflake();
    private AtomicInteger makeOrderCount = new AtomicInteger(0);

    private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);

    @Resource(name = "matchOrderQueue")
    LinkedBlockingQueueMap<OrderMessage> matchOrderQueue;
    @Autowired
    ColaExchangeBiz biz;
    @Autowired
    MatchService service;
    @Autowired
    InFluxDbService inFluxDbService;
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    AccountService accountService;
    @Autowired
    ColaSymbolBiz symbolBiz;
    @Autowired
    UserAuthUtil userAuthUtil;
    @Autowired
    UserAuthConfig userAuthConfig;
    @Autowired
    KlineService klineService;

    @Autowired
    BalanceService balanceService;

    public AppResponse getKlines(String pair, Long start, Long end, Integer limit, String type, String code) {
        if (code != null) pair = code;
        if (StringUtils.isAnyBlank(pair, type)) {
            return AppResponse.paramsError();
        }
        List<Number[]> kline = biz.kline(pair, start, end, limit, type);
        return AppResponse.ok().data(kline);
    }
    public AppResponse getDepthData(String pair, Integer length, Integer scale) {
        ColaCoinSymbol colaCoinSymbol = symbolBiz.getPair(pair);
        if (scale == null || scale == 0) scale = colaCoinSymbol.getPriceScale();
        if (length == null || length == 0) length = 1000;
        Map<String, Object> map = new HashMap<>();
        List<BigDecimal[]> ask = MatchService.sellDepth.get(pair).getDepth(length, scale);
        Collections.reverse(ask);
        List<BigDecimal[]> bids = MatchService.buyDepth.get(pair).getDepth(length, scale);
        map.put("ask", ask);
        map.put("bids", bids);
        map.put("current", biz.getMarketByPair(pair, null));
        return AppResponse.ok().data(map);
    }

    public AppResponse placeOrder(@RequestBody MakeOrderParams makeOrderParams) {
//        if (index > 500) {
//            return AppResponse.error(ColaLanguage.get(ColaLanguage.SYSTEM_BUSY));
//        }
        int i = 0;
        String orderId = snowflake.nextIdStr();



        ColaCoinSymbol colaCoinSymbol = symbolBiz.getPair(makeOrderParams.getPair());
        if(makeOrderParams.getType().equals(OrderDirection.BUY)&&colaCoinSymbol.getAllowBuy()==0){
            return AppResponse.error(ColaLanguage.get(ColaLanguage.FORBIDDEN));
        }
        if(makeOrderParams.getType().equals(OrderDirection.SELL)&&colaCoinSymbol.getAllowSell()==0){
            return AppResponse.error(ColaLanguage.get(ColaLanguage.FORBIDDEN));
        }


        // 截断价格数量
        if (!assertGreaterThanZero(makeOrderParams.getPrice()) || !assertGreaterThanZero(makeOrderParams.getNumber())) {
            return AppResponse.error(ColaLanguage.get(ColaLanguage.FORBIDDEN));
        }
        OrderMessage order = new OrderMessage();
        order.setPrice(makeOrderParams.getPrice().setScale(colaCoinSymbol.getPriceScale(), RoundingMode.DOWN));
        order.setNumber(makeOrderParams.getNumber().setScale(colaCoinSymbol.getAmountScale(), RoundingMode.DOWN));
        if (order.getPrice().compareTo(BigDecimal.ZERO) == 0 || order.getNumber().compareTo(BigDecimal.ZERO) == 0) {
            return AppResponse.paramsError();
        }

        order.setRemain(order.getNumber());
        order.setId(orderId);
        order.setTimestamp(System.currentTimeMillis());
        order.setUserId(BaseContextHandler.getUserID());
        order.setType(OrderType.LIMIT);
        order.setDirection(makeOrderParams.getType());
        order.setPair(makeOrderParams.getPair());
        if (!MatchService.running.get(order.getPair())) {
            throw new RuntimeException("撮合未启动");
        }
        i = biz.makeOrder(BaseContextHandler.getUserID(),order, colaCoinSymbol.getFees());
        if (i == -1) {
            return AppResponse.error(ResponseCode.NO_ENOUGH_MONEY_CODE, ResponseCode.NO_ENOUGH_MONEY_MESSAGE);
        }
        if (i == 0) {
            log.error("下单失败");
            return AppResponse.ok().data(orderId);
        }

        return AppResponse.ok().data(orderId);
    }

    public AppResponse doCancel(@RequestBody Map<String, String> params) {
        try {
            String userId = BaseContextHandler.getUserID();
            String orderId = params.get("orderId");
            String pair = params.get("pair");
            if (StringUtils.isBlank(orderId) && StringUtils.isNotBlank(pair)) {
                // 批量撤单
                List<OrderMessage> orders = orderMapper.selectOrderByPair(pair, userId);
                for (OrderMessage order : orders) {
                    if(!(order.getStatus().equals(OrderStatus.FULL_COMPLETED) || order.getStatus().equals(OrderStatus.FULL_CANCELLED) || order.getStatus().equals(OrderStatus.PARTIAL_CANCELLED)))
                    biz.cancelOrder(order);
                }
            } else if(!StringUtils.isEmpty(orderId)){
                OrderMessage entity = orderMapper.selectByPrimaryKey(orderId);
//               操作人信息
                Assert.notNull(userId, "操作人id不能为空");
//                User operateUser = userMapper.selectByPrimaryKey(userId);
//                // 是否admin
//                if (!"ADMIN".equals(operateUser.getType()) && !operateUser.getId().equals(entity.getUserId())) {
//                    return AppResponse.error(ColaLanguage.get(ColaLanguage.FORBIDDEN));
//                }
                if (entity.getStatus().equals(OrderStatus.FULL_COMPLETED) || entity.getStatus().equals(OrderStatus.FULL_CANCELLED) || entity.getStatus().equals(OrderStatus.PARTIAL_CANCELLED)) {
                    return AppResponse.ok();
                }
                biz.cancelOrder(entity);
            }else {
                List<OrderMessage> orders = orderMapper.selectOrderByPair(null, userId);
                for (OrderMessage order : orders) {
                    if(!(order.getStatus().equals(OrderStatus.FULL_COMPLETED) || order.getStatus().equals(OrderStatus.FULL_CANCELLED) || order.getStatus().equals(OrderStatus.PARTIAL_CANCELLED)))
                        biz.cancelOrder(order);
                }


            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return AppResponse.ok();
    }

    public List<OrderMessage> getOrderMessages(String pair, Long timestamp, Integer size, String type, Integer isPending) {
        String userId = BaseContextHandler.getUserID();
        if (timestamp == null || timestamp == 0) {
            timestamp = System.currentTimeMillis();
        }
        if (isPending == null || isPending == 0) {
            isPending = 0;
        }
        if (size == null || size == 0) {
            size = 10;
        }
        return biz.getPersonOrder(userId, timestamp, pair, type, size, isPending);
    }

    private boolean assertGreaterThanZero(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return false;
        }
        return true;
    }

    public String makeSign(Map<String,String> makeOrderParams, String passwordSign) {
        SortedMap<String, String> paramsMaps = new TreeMap<>();
        for (String field : makeOrderParams.keySet()) {
            if (!field.equals("sign")) {
                Object value =makeOrderParams.get(field);
                if (value != null) {
                    String valueStr = null;
                    if (value instanceof BigDecimal) {
                        valueStr = ((BigDecimal) value).stripTrailingZeros().toPlainString();
                    } else {
                        valueStr = value.toString();
                    }
                    paramsMaps.put(field, valueStr);
                }
            }
        }
        paramsMaps.put("ApiSecret", passwordSign);
        String signStr = makeSignStr(paramsMaps);
        log.info("签名字符串: "+signStr);
        return Objects.requireNonNull(MD5Utils.MD5(signStr)).toLowerCase();
    }

    private String makeSignStr(SortedMap<String, String> params) {
        StringBuilder stringBuilder = new StringBuilder(120);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String value = entry.getValue();
            stringBuilder.append(entry.getKey()).append("=").append(value).append("&");
        }
        return stringBuilder.substring(0, stringBuilder.length() - 1);

    }
    public OrderMessage details(String id){
        return orderMapper.selectByPrimaryKey(id);


    }
}
